/*
 * Copyright 2011 Christoph Ludwig (christoph[at]chludwig.de). All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.chludwig.ehumanities.provenance.entities;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;

import javax.xml.datatype.XMLGregorianCalendar;

import de.chludwig.ehumanities.provenance.jaxb.WorkflowContainerInitializerType;
import de.chludwig.ehumanities.provenance.jaxb.WorkflowContainerMetadataType;
import de.chludwig.ehumanities.provenance.persistence.StorageArea;
import de.chludwig.ehumanities.provenance.persistence.WorkflowContainerPO;
import de.chludwig.ehumanities.provenance.utils.CalendarUtil;

/**
 * Container that hols all information submitted during a specific workflow
 * execution.
 */
public class WorkflowContainer {

  private WorkflowContainerMetadataType metadata;

  private WorkflowContainerPO containerPO;

  static public WorkflowContainer generateNewContainer(WorkflowContainerInitializerType containerInitializer) {
    UUID identifier = UUID.randomUUID();
    XMLGregorianCalendar timestamp = CalendarUtil.getInstance().currentTimestamp();

    WorkflowContainerMetadataType workflowMetadata = new WorkflowContainerMetadataType();
    workflowMetadata.setCreator(containerInitializer.getCreator());
    workflowMetadata.setUuid(identifier);
    workflowMetadata.setTimestamp(timestamp);

    WorkflowContainer result = new WorkflowContainer();
    result.setMetadata(workflowMetadata);

    WorkflowContainerPO containerPO = containerPOForId(identifier);
    containerPO.persistContainer(result);

    return result;
  }

  static public WorkflowContainer containerWithId(UUID uuid) {
    WorkflowContainerPO containerPO = containerPOForId(uuid);
    WorkflowContainerMetadataType workflowMetadata = containerPO.readMetadata();

    WorkflowContainer result = new WorkflowContainer();
    result.setMetadata(workflowMetadata);
    return result;
  }

  /**
   * @param identifier
   * @return
   */
  private static WorkflowContainerPO containerPOForId(UUID identifier) {
    StorageArea storageArea = new StorageArea();
    return new WorkflowContainerPO(storageArea, identifier);
  }

  /**
   * @return the metadata
   */
  public WorkflowContainerMetadataType getMetadata() {
    return metadata;
  }

  public UUID getContainerId() {
    return getOrCreateMetadata().getUuid();
  }

  public XMLGregorianCalendar getTimestamp() {
    return getOrCreateMetadata().getTimestamp();
  }

  public String getCreator() {
    return getOrCreateMetadata().getCreator();
  }

  public List<LogEntry> getLogEntries() {
    WorkflowContainerPO po = getContainerPO();
    List<UUID> entryIds = po.getLogEntryIds();
    List<LogEntry> result = new ArrayList<LogEntry>(entryIds.size());
    for (UUID entryId : entryIds) {
      LogEntry entry = LogEntry.existingLogEntry(this, entryId);
      result.add(entry);
    }
    return result;
  }

  public Archive moveToArchive(ExecutorService executor) {
    getContainerPO().toArchive();

    Archive archive = Archive.archiveWithId(getContainerId());
    archive.startZipTask(executor);

    return archive;
  }

  public void sweepIfStale(Date cutoffTimestamp) {
    getContainerPO().sweepIfStale(cutoffTimestamp);
  }

  /**
   * @param workflowMetadata
   *          the metadata to set
   */
  private void setMetadata(WorkflowContainerMetadataType workflowMetadata) {
    this.metadata = workflowMetadata;
  }

  private WorkflowContainerMetadataType getOrCreateMetadata() {
    if (metadata == null) {
      metadata = new WorkflowContainerMetadataType();
    }
    return metadata;
  }

  /**
   * @return the containerPO
   */
  private WorkflowContainerPO getContainerPO() {
    if (containerPO == null) {
      containerPO = containerPOForId(getContainerId());
    }
    return containerPO;
  }

}
